try-catch
機制對巨集 ##
不熟悉的:
Day 21:重溫前置處理器、巨集( #, ## )、預先定義的巨集
對可變參數宏 __VA_ARGS__
不熟悉的:
Day 22:重溫 可變參數函數、可變參數宏 __VA_ARGS__
對 goto
的用法不熟悉的:
Day 23: goto、標記、爭議及反面例子
之所以說是極粗略實現(標題),是因為在這天之前,沒有真正動手實作例外處理。
只有構思及想法,所以本系列的 例外處理系列 屬於 【實驗性質】,
可否完美/真正實現還是未知之數,需要研究、嘗試以及逐步完善,
所以請不要以此作參考、樣本或榜樣。
除了 【實驗性質】 也存在著 【記錄性質】,
記錄研究、嘗試實作的過程,而這亦是自我挑戰的一部分。
目前本系列的實現有一個前提,或者說編程風格約定,
就是函數的回傳值只用作錯誤碼檢測。
默認回傳 -1
代表發生錯誤, 0
代表正常。
這意味輸出數值必須使用按址傳值,不可使用回傳。
以下有巨集 try
, catch
, throw
的實現,
先說 try
和 throw
這兩個比較簡單的實現:
#define try(function, ex_name) \
\
if(function == -1){ \
goto ex_name; \
} \
back_to_##ex_name:
#define throw(function) \
\
if(function == -1){ \
return -1; \
}
巨集 try
有兩個參數,function
和 ex_name
,
一個是會回傳錯誤的函數,
另一個是 例外(exception)的名稱,名稱是自己作的。
根據約定,回傳值只用作錯誤檢測, -1
代表錯誤。
所以 巨集 try
檢測函數的回傳是否等於 -1
,即有否發生錯誤。
若是,則 goto
到 catch
的部分。
安排了標記 back_to_##ex_name:
來讓 catch
返回原本位置。
而 throw
的部分更加簡單,
檢測函數的回傳是否等於 -1
,
若是,則 return -1;
,根據約定,即等於拋出錯誤。
catch
的部分稍微複雜一點:#define catch(ex_name, ...) \
\
goto ex_name##_end; \
ex_name: \
__VA_ARGS__ \
goto back_to_##ex_name; \
ex_name##_end:
注意 \
的前面有空格,為避免前後文連在一起,區分出識別字。
巨集 catch
有兩個參數,一個是 try
產生的例外名稱,
另一個是 可變參數,作為輸入錯誤處理的代碼。
共有五行:
goto ex_name##_end;
ex_name:
__VA_ARGS__
goto back_to_##ex_name;
ex_name##_end:
第二行 ex_name:
是 try
產生的例外名稱 的標記,
用於發生錯誤後,被跳轉的位置。
第三行 __VA_ARGS__
是 可變參數宏,
會原封不漏地把錯誤處理的代碼搬到這裏。
第四行 goto back_to_##ex_name;
用於返回發生錯誤的位置。
即對應 try
的標記。
若果程序正常地運行 catch
到語句,
第一行會把程序跳轉到第五行,使其跳過 錯誤處理 的部分。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*... 巨集 try, catch, throw 的宣告 ...*/
int can_not_be_negative(double input) {
return input < 0 ? -1 : 0;
}
int my_sqrt(double *output, double input) {
throw(can_not_be_negative(input));
*output = sqrt(input);
return 0;
}
int main() {
double get_output;
try(my_sqrt(&get_output, -10), ex_neg);
/*... any things ...*/
catch(ex_neg, printf("Input can't no be negative.\n"); );
system("pause");
return 0;
}
Input can't no be negative.
Press any key to continue . . .
函數 can_not_be_negative
會返回錯誤(-1
),
函數 my_sqrt
會用 throw
拋出 函數 can_not_be_negative
產生的錯誤,try
接收了 my_sqrt
拋出的錯誤,
程序跳過 /*... any things ...*/
,到 catch
進行錯誤處理,
返回 try
後的語句。
主程序可改寫成比較像 try{}-catch{}
的區塊形式,稍微增加可讀性:
int main() {
double get_output;
try(
my_sqrt(&get_output, -10),
ex_neg);
/*... any things ...*/
catch(ex_neg,
printf("Input can't no be negative.\n");
);
system("pause");
return 0;
}
目前有不少問題,例如一個 try
必須獨立對應一個 catch
,
並且當中的 例外名稱 需要唯一。
故此 多個同一類型的錯誤 會產生多個重複語句。
十個 try
有 十個 catch
。